package com.test.mq.config;


import com.test.mq.listenner.ConfirmCallBackListener;
import com.test.mq.listenner.ReturnCallBackListener;
import org.apache.log4j.Logger;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.PublisherCallbackChannelImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

/**
 * 消息队列的使用步骤:
 * （1）客户端连接到消息队列服务器，打开一个channel。
 * （2）客户端声明一个exchange，并设置相关属性
 * （3）客户端声明一个queue，并设置相关属性。
 * （4）客户端使用routing key，在exchange和queue之间建立好绑定关系。
 * （5）客户端投递消息到exchange。
 */
@Configuration
public class RabbitMQConfiguration {
    private static Logger logger = Logger.getLogger(RabbitMQConfiguration.class);
    @Value("${spring.rabbitmq.host:127.0.0.1}")
    private String host;
    @Value("${spring.rabbitmq.port:5678}")
    private int port;
    @Value("${spring.rabbitmq.username:guest}")
    private String username;
    @Value("${spring.rabbitmq.password:guest}")
    private String password;
    /*
     * 开启发送确认  默认为 true
     */
    @Value("${spring.rabbitmq.publisher-confirms:true}")
    private boolean publisherConfirms;
    /*
     * 开启发送失败退回
     */
    @Value("${spring.rabbitmq.publisher-returns:true}")
    private boolean publisherReturns;
    // 注册链接信息
    @Bean
    public ConnectionFactory connectionFactory() {
        logger.info("============== MQtest ========host="+host+"======= port = " + port +"user =" + username);
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost("/"); // 设置虚拟机

        /** 如果要进行  消息回调,确认消息，则这里必须要设置为true */
        //对发布者的代理确认  确认消息 是否到达 目标交换器
        connectionFactory.setPublisherConfirms(publisherConfirms);
        // 确认消息 未到达 目标(队列) 是否返回
        connectionFactory.setPublisherReturns(publisherReturns);
        /*
        通过将cachingConnectionFactory的PublisherConfirms和PublisherReturns属性分别设置为“true”，支持已确认和返回的消息。
        设置这些选项后，工厂创建的通道将包装在 PublisherCallbackChannel 中，用于方便回调。
        当获得这样一个通道时，客户端可以用该通道注册 PublisherCallbackChannel.Listener。
        PublisherCallbackChannel实现包含将确认/返回路由到适当侦听器的逻辑。
         */
        logger.info("Create ConnectionFactory bean ..");

        return connectionFactory;
    }

    /**
     * 注册RabbitTemplate用于提供API操作
     * 发送消息设置发送模式deliveryMode=2代表持久化消息
     * RabbitTemplate默认情况下发送模式为deliveryMode=2
     * 因为要设置回调类，所以 @Scope 应是prototype类型，如果是singleton类型，则回调类为最后一次设置
     * @return RabbitTemplate
     */
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)//此处必须是prototype类型
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate template = new RabbitTemplate(connectionFactory());

        // 配置 消息到达 交换器(exchange) 后 到达 队列(queue) 则进行消息回调。 为false或者不设置，则不回回调,消息丢失
        template.setMandatory(true);

        // 设置 消息到达  交换器 监听
        template.setConfirmCallback(new ConfirmCallBackListener());
        // 设置 消息 从交换器 分发到 队列 监听
        template.setReturnCallback(new ReturnCallBackListener());
        return template;
    }




}
